#ifndef DEFORMABLE_MESH_H
#define DEFORMABLE_MESH_H

#include <list>

#include "model_obj.h"
#include "vmatrix.h"
#include "vertexcluster.h"

#include "graph.h"

enum PlasticityMode
{
	PLASTICITY_MODE_NONE,
	PLASTICITY_MODE_UPDATE,
	PLASTICITY_MODE_REMODEL
};

enum DeformationMode
{
	DEFORMATION_MODE_LINEAR,
	DEFORMATION_MODE_QUADRATIC
};

enum FractureMode
{
	FRACTURE_MODE_NO_FRACTURES,
	FRACTURE_MODE_SURFACE
};

enum VertexBlockMode
{
	VERTEX_BLOCK_MODE_FREE = 0,
	VERTEX_BLOCK_MODE_LOCK_X = 1,
	VERTEX_BLOCK_MODE_LOCK_Y = 2,
	VERTEX_BLOCK_MODE_LOCK_Z = 4,
	VERTEX_BLOCK_MODE_LOCK_ALL = 7
};

enum SimulationState 
{
	SIMULATION_STATE_STOPPED,
	SIMULATION_STATE_APPLYING_FORCES,
	SIMULATION_STATE_COLLISION_CONSTRAINTS,
	SIMULATION_STATE_DEFORMABLE_BODY_CONSTRAINTS,
	SIMULATION_STATE_INTEGRATION_FINISH
};

struct Color 
{
	float color[3];
};

struct MeshArc
{
public:
	int index;
	float length;

	 bool operator== (const MeshArc &other) const {
		return index == other.index;
	 }
};

//class Arcs
//{
//public:
//	int v1Id;
//	std::vector<MeshArc> arcs;
//
//	void AddArc( MeshArc& arc )
//	{
//		for( int i = 0; i < arcs.size(); i++ )
//		{
//			if( arcs[ i ].index == arc.index )
//				return;
//		}
//
//		arcs.push_back( arc );
//	}
//};

class DeformableMesh //: public ModelOBJ
{
public:
	static const float FLOOR_HEIGHT;

	DeformableMesh();
	DeformableMesh( DeformationMode mode, float rigidity );

	bool import( const char* fileName );

	void createArcs();

	void update( float dt );

	void moveMesh( float dx, float dy );
	void moveVertex( int vertexId, float dx, float dy, float dz );

	void rotateVertex( int vertexIndex, float angle );

	void setVertexBlockMode( int vertexIndex, int blockMode );

	float getRigidity() const { return rigidity; }
	void setRigidity( float rigidity );

	float getBeta();
	float getAlpha();
	
	int getNumberOfVertices()
	{
		return m_vertexBuffer.size();
	}

	ModelOBJ::Vertex& getVertex( int index ) 
	{
		return m_vertexBuffer[ index ];
	}

	int addVertex( ModelOBJ::Vertex& v )
	{
		m_vertexBuffer.push_back( v );
		numberOfVertices++;
		return m_vertexBuffer.size() - 1;
	}

	PlasticityMode getPlasticityMode() const { return plasticityMode; }
	void setPlasticityMode( PlasticityMode plasticityMode ) { this->plasticityMode = plasticityMode; }

	ModelOBJ::Vertex& getVertexForDrawing( int index ) 
	{
		if( simulationState == SIMULATION_STATE_DEFORMABLE_BODY_CONSTRAINTS || simulationState == SIMULATION_STATE_COLLISION_CONSTRAINTS )
			return intermediatePosition[ index ];
		else
			return m_vertexBuffer[ index ];
	}

	inline void setSimulationStepByStep( bool isSimulationStepByStep )
	{
		this->isSimulationStepByStep = isSimulationStepByStep;
	}

	inline bool getSimulationStepByStep() const
	{
		return isSimulationStepByStep;
	}

	Color* getColorBuffer();
	ModelOBJ::Vertex* getVertexBufferForDrawing();

	bool getRecalculateWeights() const 
	{ 
		return recalculateWeights;
	}

	void setRecalculateWeights(bool recalculateWeights) 
	{ 
		this->recalculateWeights = recalculateWeights;

		for( int i = 0; i < numberOfClusters; i++ )
		{
			clusters[ i ]->setRecalculateWeights( recalculateWeights );
		}
	}

	static float vertexDistance( ModelOBJ::Vertex& v1, ModelOBJ::Vertex& v2 );
	void getCenterOfMass( float* x, float* y, float* z );

protected:

	static const int RELAXATION_MAX_ITERATIONS = 1;
	static const float GRAVITY_ACC;
	static const float RESTITUTION;
	static const float ACCEPTABLE_DEEPNESS;
	static const float ENERGY_PRESERVATION;
	static const float INTERPENETRATION_CORRECTION_FACTOR;

	float rigidity;
	float alpha;
	float beta;

	DeformationMode deformationMode;
	PlasticityMode plasticityMode;
	FractureMode fractureMode;
	
	bool isSimulationStepByStep;
	SimulationState simulationState;

	int numberOfClusters;

	std::map< int, std::list<MeshArc> > arcs;

	std::vector<VertexCluster*> clusters;

	// cluster id for each vertex
	std::vector<std::vector<std::pair<int,int>>> vertexClusters;

	// cache for total number of vertices of the model
	int numberOfVertices;

	std::vector<ModelOBJ::Vertex> m_vertexBuffer;
	std::vector<ModelOBJ::Vertex> vSpeed;

	std::vector<ModelOBJ::Vertex> intermediatePosition;
	std::vector<ModelOBJ::Vertex> intermediateSpeed;

	std::vector<Color> colorBuffer;

	// says whether the vertex can be moved by external forces
	std::vector< int > vertexBlockedStatus;

	bool weightHasChanged;
	bool recalculateWeights;
	float deepness;
	bool shouldRecalculate;
	//float move[2];
	float inline calculateRigidityFactor();

	//void updatePhysicalSystem( float dt );
	//void updateDeformationSystem( float dt );
	void reset( DeformationMode mode, float rigidity );

	void calculateAlphaAndBeta();

	void applyForces( float dt );

	void updateDeepness( float yPosition );

	void updateContinuous( float dt );

	void passToIntermediateValues();

	//void applyConstraints( float dt );
	void finishIntegration( float dt );

	//float getDeepness();

	void applyCollisionConstraints( float dt );

	void preparePositionAndSpeed();

	float calculateNewVertexWeight( int vertexIndex  );

	void applyDeformableConstraints( float dt );
	//float getIntermediateDeepness();
	void loadingVertexWeightInformation( std::ifstream& fileHandle );
	bool checkForFractures();
	
	
};
#endif

